function createStore(reducer, preloadedState, enhancer) {
  if (typeof reducer !== 'function') {
    throw new Error('reducer is not a function')
  }

  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('enhancer is not a function')
    }

    return enhancer(createStore)(reducer, preloadedState)
  }

  var currentState = preloadedState
  var currentListeners = []

  function getState() {
    return currentState
  }

  function dispatch(action) {
    if (!isPlainObject(action)) {
      throw new Error('action is not a object')
    }

    if (typeof action.type === 'undefined') {
      throw new Error('action has no property "type"')
    }

    currentState = reducer(currentState, action)

    for (var i = 0; i < currentListeners.length; i++) {
      var listener = currentListeners[i]

      listener()
    }
  }

  function subscribe(listeners) {
    currentListeners.push(listeners)
  }

  return {
    getState,
    dispatch,
    subscribe
  }
}

// 判断 obj 是否是对象
function isPlainObject(obj) {
  // 排除基本数据类型和 null
  if (typeof obj !== 'object' || obj === null) {
    return false
  }

  // 区分数组和对象 原型对象对比的方式
  var proto = obj

  while (Object.getPrototypeOf(proto) !== null) {
    proto = Object.getPrototypeOf(proto)
  }

  return Object.getPrototypeOf(obj) === proto
}

function applyMiddleware(...middlewares) {
  return function (createStore) {
    return function (reducer, preloadedState) {
      var store = createStore(reducer, preloadedState)

      var middlewareAPI = {
        getState: store.getState,
        dispatch: store.dispatch
      }

      var chain = middlewares.map(middleware => middleware(middlewareAPI))

      var dispatch = compose(...chain)(store.dispatch)

      return {
        ...store,
        dispatch
      }
    }
  }
}

function compose() {
  var funcs = [...arguments]

  return function (dispatch) {
    for (var i = funcs.length - 1; i >= 0; i--) {
      dispatch = funcs[i](dispatch)
    }

    return dispatch
  }
}

function bindActionCreators(actionCreators, dispatch) {
  var boundActionCreators = {};

  for (var key in actionCreators) {
    (function (key) {
      boundActionCreators[key] = function () {
        dispatch(actionCreators[key]())
      }
    })(key)
  }

  return boundActionCreators;
}

function combineReducers(reducers) {
  var reducerKeys = Object.keys(reducers)

  for (var i = 0; i < reducerKeys.length; i++) {
    var key = reducerKeys[i];

    if (typeof reducers[key] !== 'function') {
      throw new Error('reducer is not a function')
    }
  }

  return function (state, action) {
    var nextState = {}

    for (var i = 0; i < reducerKeys.length; i++) {
      var key = reducerKeys[i]
      var reducer = reducers[key]
      var previousStateForKey = state[key]

      nextState[key] = reducer(previousStateForKey, action)
    }

    return nextState;
  }
}